package com.kalvan.log.logback.web;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ResourceBundle;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.commons.lang.StringUtils;
import org.slf4j.impl.StaticLoggerBinder;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.classic.util.ContextSelectorStaticBinder;
import ch.qos.logback.core.joran.spi.JoranException;

/**
 * Bootstrap listener for custom Logback initialization in a web environment.
 * Delegates to WebLogbackConfigurer (see its javadoc for configuration
 * details).
 * <p/>
 * <b>WARNING: Assumes an expanded WAR file</b>, both for loading the
 * configuration file and for writing the log files. If you want to keep your
 * WAR unexpanded or don't need application-specific log files within the WAR
 * directory, don't use Logback setup within the application (thus, don't use
 * Log4jConfigListener or LogbackConfigServlet). Instead, use a global, VM-wide
 * Log4J setup (for example, in JBoss) or JDK 1.4's
 * <code>java.util.logging</code> (which is global too).
 * <p/>
 * This listener should be registered before ContextLoaderListener in web.xml,
 * when using custom Logback initialization.
 * <p/>
 * For Servlet 2.2 containers and Servlet 2.3 ones that do not initialize
 * listeners before servlets, use LogbackConfigServlet. See the
 * ContextLoaderServlet javadoc for details.
 * 
 * @see LogbackConfigListener
 *
 */
public class LogbackConfigListener implements ServletContextListener {
	public static final String LOGPATH = "logback.logpath";
	public static final String ROOTLOGGER_LEVEL = "logback.rootLoggerLevel";
	public static final String MAXFILESIZE = "logback.maxFileSize";
	public static final String LOGBACKPROPERTIES = "logbackProperties";

	public static final String CONFIG_LOCATION_PARAM = "logbackConfigLocation";

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	public static final String CLASSPATH_URL_PREFIX = "classpath:";

	/**
	 * 注入动态配置属性<br>
	 * 编码默认是utf-8 <br>
	 * 输出格式统一%date{yyyy-MM-dd HH:mm:ss.SSS}|%-5level
	 * |%t:%threadid|%logPreFix|%class.%method:%line|%sensitiveMsg%n
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		ServletContext context = event.getServletContext();
		// 查找logback属性配置文件
		if (StringUtils.isNotBlank(context.getInitParameter(LOGBACKPROPERTIES))) {
			String path = context.getInitParameter(LOGBACKPROPERTIES);
			if (path.endsWith(".properties")) {
				path = path.replace(".properties", "");
			}
			ResourceBundle resource = ResourceBundle.getBundle(path);
			System.setProperty(LOGPATH, resource.getString(LOGPATH).trim());
			System.setProperty(ROOTLOGGER_LEVEL,
					resource.getString(ROOTLOGGER_LEVEL).trim());
			System.setProperty(MAXFILESIZE, resource.getString(MAXFILESIZE)
					.trim());
		} else {
			// 注入动态配置属性-日志路径
			if (StringUtils.isNotBlank(context.getInitParameter(LOGPATH))) {
				System.setProperty(LOGPATH, context.getInitParameter(LOGPATH)
						.trim());
			}
			// 注入动态配置属性-root日志级别
			if (StringUtils.isNotBlank(context
					.getInitParameter(ROOTLOGGER_LEVEL))) {
				System.setProperty(ROOTLOGGER_LEVEL,
						context.getInitParameter(ROOTLOGGER_LEVEL).trim());
			}
			// 注入动态配置属性-文件分隔大小
			if (StringUtils.isNotBlank(context.getInitParameter(MAXFILESIZE))) {
				System.setProperty(MAXFILESIZE,
						context.getInitParameter(MAXFILESIZE).trim());
			}
		}
		String location = context.getInitParameter(CONFIG_LOCATION_PARAM);
		/***** 方法一加载，使用原生方式 *******/
		try {
			URL url = getURL(location);
			LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder
					.getSingleton().getLoggerFactory();
			// in the current version logback automatically configures at
			// startup
			// the context, so we have to reset it
			loggerContext.reset();
			// reinitialize the logger context. calling this method allows
			// configuration through groovy or xml
			new ContextInitializer(loggerContext).configureByResource(url);
		} catch (FileNotFoundException ex) {
			context.log("No logback configuration file found at [" + location
					+ "]");
			// throw new
			// IllegalArgumentException("Invalid 'logbackConfigLocation' parameter: "
			// + ex.getMessage());
		} catch (JoranException e) {
			throw new RuntimeException(
					"Unexpected error while configuring logback", e);
		}

		/***** 方法二加载，使用spring方式 *******/
		/**
		 * sring 集成 时使用 <dependency> <groupId>org.logback-extensions</groupId>
		 * <artifactId>logback-ext-spring</artifactId> </dependency>
		 */
		// WebLogbackConfigurer.initLogging(event.getServletContext());
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		// WebLogbackConfigurer.shutdownLogging(event.getServletContext());
		ContextSelector selector = ContextSelectorStaticBinder.getSingleton()
				.getContextSelector();
		LoggerContext loggerContext = selector.getLoggerContext();
		String loggerContextName = loggerContext.getName();
		LoggerContext context = selector.detachLoggerContext(loggerContextName);
		context.reset();
	}

	/**
	 * Resolve the given resource location to a {@code java.net.URL}.
	 * <p>
	 * Does not check whether the URL actually exists; simply returns the URL
	 * that the given location would correspond to.
	 * 
	 * @param resourceLocation
	 *            the resource location to resolve: either a "classpath:" pseudo
	 *            URL, a "file:" URL, or a plain file path
	 * @return a corresponding URL object
	 * @throws FileNotFoundException
	 *             if the resource cannot be resolved to a URL
	 */
	private static URL getURL(String resourceLocation)
			throws FileNotFoundException {
		if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
			String path = resourceLocation.substring(CLASSPATH_URL_PREFIX
					.length());
			URL url = Thread.currentThread().getContextClassLoader()
					.getResource(path);
			if (url == null) {
				String description = "class path resource [" + path + "]";
				throw new FileNotFoundException(
						description
								+ " cannot be resolved to URL because it does not exist");
			}
			return url;
		}
		try {
			// try URL
			return new URL(resourceLocation);
		} catch (MalformedURLException ex) {
			// no URL -> treat as file path
			try {
				return new File(resourceLocation).toURI().toURL();
			} catch (MalformedURLException ex2) {
				throw new FileNotFoundException("Resource location ["
						+ resourceLocation
						+ "] is neither a URL not a well-formed file path");
			}
		}
	}
}